﻿/*
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Management;
using HyperVManager.Model;

namespace HyperVManager
{
    public partial class _Default : System.Web.UI.Page
    {
        /// <summary>
        /// List of all VM Hosts
        /// </summary>
        List<Host> vmList;

        /// <summary>
        /// Hyper-V Management Scope
        /// </summary>
        ManagementScope manScope;

        /// <summary>
        /// Load the vmList and databind the datagrid.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_Load(object sender, EventArgs e)
        {
            manScope = new ManagementScope(@"\\.\root\virtualization");

            vmList = getHosts();

            if (!IsPostBack)
            {
                VMDataGrid.DataSource = vmList;
                VMDataGrid.DataBind();
            }
        }

        /// <summary>
        /// Get all VM hosts for a certain scope
        /// </summary>
        /// <param name="scopePath">ManagementScope</param>
        /// <returns>List of Host objects</returns>
        private List<Host> getHosts()
        {
            // query all virtual machines on the host
            ObjectQuery vmQuery = new ObjectQuery("SELECT * FROM Msvm_ComputerSystem WHERE Description = 'Microsoft Virtual Machine'");
            ManagementObjectSearcher vmSearcher = new ManagementObjectSearcher(manScope, vmQuery);
            ManagementObjectCollection vmCollection = vmSearcher.Get();

            List<Host> vmList = new List<Host>();

            foreach (ManagementObject vm in vmCollection)
            {
                Host host = new Host();
                host.VmManagementObject = vm;
                setHostInfo(host);
                vmList.Add(host);
            }

            vmList.Sort(CompareByName);

            return vmList;
        }

        /// <summary>
        /// Set properties for a host
        /// </summary>
        /// <param name="host">The Host</param>
        protected void setHostInfo(Host host)
        {
            ManagementObject vm = host.VmManagementObject;
            host.Name = vm["ElementName"].ToString();
            host.State = GetStatusString(vm["EnabledState"].ToString());

            long uptime = long.Parse(vm["OnTimeInMilliseconds"].ToString());
            if (uptime > 0)
            {
                int days = (int)(uptime / (24 * 60 * 60 * 1000));
                int hours = (int)(uptime / (60 * 60 * 1000)) % 24;
                int minutes = (int)(uptime / (60 * 1000)) % 60;
                //int seconds = (uptime / (1000)) % 60;
                host.UpTime = String.Format("{0} day(s), {1:00} hours, {2:00} minutes", days, hours, minutes);
            }

            // get the processor for a VM
            ObjectQuery procQuery = new ObjectQuery("SELECT * FROM MSVM_Processor WHERE SystemName = '" + vm["Name"].ToString() + "'");
            ManagementObjectSearcher procSearcher = new ManagementObjectSearcher(manScope, procQuery);
            ManagementObjectCollection procCollection = procSearcher.Get();

            foreach (ManagementObject proc in procCollection)
            {
                host.CpuLoad = proc["LoadPercentage"].ToString() + "%";
            }

            // get the memory for a VM
            ObjectQuery memQuery = new ObjectQuery("SELECT * FROM Msvm_Memory WHERE SystemName = '" + vm["Name"].ToString() + "'");
            ManagementObjectSearcher memSearcher = new ManagementObjectSearcher(manScope, memQuery);
            ManagementObjectCollection memCollection = memSearcher.Get();

            foreach (ManagementObject mem in memCollection)
            {
                // TODO: is this always in MBs?
                host.Memory = mem["NumberOfBlocks"].ToString() + " MB";
            }
        }

        /// <summary>
        /// Refresh the properties of each Host
        /// </summary>
        protected void refreshVmList()
        {
            foreach (Host host in vmList)
            {
                setHostInfo(host);
            }
        }

        /// <summary>
        /// Refresh the properties of each Host and refresh the datagrid.
        /// </summary>
        protected void refreshVMDataGrid()
        {
            refreshVmList();

            VMDataGrid.DataSource = vmList;
            VMDataGrid.DataBind();
        }

        /// <summary>
        /// Handler for refresh button
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        protected void RefreshButton_OnClick(object source, System.EventArgs e)
        {
            refreshVMDataGrid();
        }

        /// <summary>
        /// Handler for datagrid events
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        protected void VMDataGrid_ItemCommand(object source, DataGridCommandEventArgs e)
        {
            Host clickedVm = vmList.ElementAt(e.Item.ItemIndex);
            ManagementBaseObject outParams;

            switch (e.CommandName)
            {
                case "StartVM":
                    outParams = clickedVm.StartVM();
                    MonitorJob(outParams, clickedVm);

                    break;
                case "StopVM":
                    outParams = clickedVm.StopVM();
                    MonitorJob(outParams, clickedVm);

                    break;
                case "PauseVM":
                    outParams = clickedVm.PauseVM();
                    MonitorJob(outParams, clickedVm);

                    break;
                case "SuspendVM":
                    outParams = clickedVm.SuspendVM();
                    MonitorJob(outParams, clickedVm);

                    break;
            }

            refreshVMDataGrid();
        }

        /// <summary>
        /// Monitor a VM Job, such as starting or suspending.
        /// </summary>
        /// <param name="outParams"></param>
        /// <param name="clickedVm"></param>
        private void MonitorJob(ManagementBaseObject outParams, Host clickedVm)
        {
            //Retrieve msvc_StorageJob path. This is a full wmi path
            string JobPath = (string)outParams["Job"];

            if (JobPath != null)
            {
                ManagementObject Job = new ManagementObject(manScope, new ManagementPath(JobPath), null);

                //Try to get storage job information
                Job.Get();
                UInt16 jobState = (UInt16)Job["JobState"];

                while (jobState == 3 || jobState == 4)
                {
                    clickedVm.Status = String.Format("In progress... {0}% completed.", Job["PercentComplete"]);
                    refreshVMDataGrid();
                    System.Threading.Thread.Sleep(200);
                    Job.Get();
                    jobState = (UInt16)Job["JobState"];
                }

                ///Figure out if job failed
                if (jobState != 7)
                {
                    UInt16 jobErrorCode = (UInt16)Job["ErrorCode"];
                    clickedVm.Status = String.Format("Error code: {0} - {1}", jobErrorCode, (string)Job["ErrorDescription"]);
                }
                else
                {
                    clickedVm.Status = "Completed";
                }
            }
        }

        /// <summary>
        /// Translate the "EnabledState" code string to something more meaningful
        /// </summary>
        /// <param name="StatusValue"></param>
        /// <returns></returns>
        static String GetStatusString(String StatusValue)
        {
            switch (StatusValue)
            {
                case "0":
                    return "Unknown";
                case "2":
                    return "Running";
                case "3":
                    return "Turned off";
                case "32768":
                    return "Paused";
                case "32769":
                    return "Suspended";
                case "32770":
                    return "Starting";
                case "32771":
                    return "Snapshotting";
                case "32772":
                    return "Migrating";
                case "32773":
                    return "Saving";
                case "32774":
                    return "Stopping";
                case "32775":
                    return "Deleted";
                case "32776":
                    return "Pausing";
                case "32777":
                    return "Resuming";
                default:
                    return "Error";
            }
        }

        /// <summary>
        /// Compare two hosts by Name
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private static int CompareByName(Host x, Host y)
        {
            // simply use the standard string comparison, so the list is alphabetical by status
            return x.Name.CompareTo(y.Name);
        }

    }
}
